package com.kyp.basic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import org.apache.hadoop.io.DoubleWritable;
import org.apache.log4j.Logger;

public class BasicCalcProcess {
	private static final Logger sLogger = Logger.getLogger(BasicCalcProcess.class);
	private Iterable<DoubleWritable> mValues;

	public static final class Builder {
		private Iterable<DoubleWritable> mValues;
		private BasicCalcProcess obj = null;

		public Builder value(Iterable<DoubleWritable> value) {
			this.mValues = value;
			return this;
		}

		public BasicCalcProcess build() {
			if (obj == null)
				obj = new BasicCalcProcess();

			obj.setValue(mValues);

			return obj;
		}
	}

	private void setValue(Iterable<DoubleWritable> value) {
		this.mValues = value;
	}

	public BasicCalcBean execute() {

		double sum = 0.0;
		double count = 0.0;
		double avg = 0.0;
		double min = 0.0;
		double max = 0.0;
		double geoMean = 0.0;
		double med = 0.0;
		double stddev = 0.0;
		double var = 0.0;

		Collection<Double> list = new ArrayList<Double>();
		// 합계 및 전체 Count 구하기
		for (DoubleWritable value : mValues) {
			sum += value.get();
			++count;

			list.add(value.get());
		}

		// 산술평균 구하기
		if (count > 0.0)
			avg = sum / count;
		else
			avg = 0.0;

		// 최대값, 최소값 구하기
		max = Collections.max(list);
		min = Collections.min(list);

		sLogger.info("MAX :: " + max +" , MIN :: " + min);
		
		// 기하평균
		geoMean = Math.exp(Math.log(sum)) / count;

		// 중앙값
		double[] dobArray = new double[list.size()];
		double sumOfDeviation = 0; // 분산을 위한 편차 합

		int tmpIdx = 0;
		for (Double db : list) {
			dobArray[tmpIdx++] = db.doubleValue();
			sumOfDeviation += Math.pow((db.doubleValue() - avg), 2);
		}
		med = getMedian(dobArray);

		// 분산
		var = sumOfDeviation / count;

		// 표준편차
		stddev = Math.sqrt(var);

		return new BasicCalcBean.Builder().sum(sum).count(count).avg(avg)
				.min(min).max(max).geoMean(geoMean).med(med).stddev(stddev)
				.var(var).build();

	}

	private double getMedian(double[] dobArray) {
		if (dobArray.length == 0)
			return 0.0;

		Arrays.sort(dobArray);

		int center = dobArray.length / 2;

		if (dobArray.length % 2 == 1) { // 요소 개수가 홀수면
			return dobArray[center]; // 홀수 개수인 배열에서는 중간 요소를 그대로 반환
		} else {
			return (dobArray[center - 1] + dobArray[center]) / 2.0; // 짝수 개 요소는,
																	// 중간 두 수의
																	// 평균 반환
		}
	}

}
